/** @file
 * comsock.c
 * @author Marco Luisi
 *  Created on: 25 May 2010
 *
 */

#include "comsock.h"
#include "msg.h"


#include <stdio.h>

#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>



//togliere poi i commenti della professoressa tanto sono nell'header



/** Crea una socket AF_UNIX
 *  \param  path pathname della socket
 *
 *  \retval s    il file descriptor della socket  (s>0)
 *  \retval SNAMETOOLONG se il nome del socket eccede UNIX_PATH_MAX
 *  \retval -1   in altri casi di errore (sets errno)
 *
 *  in caso di errore ripristina la situazione inziale: rimuove eventuali socket create e chiude eventuali file descriptor rimasti aperti
 */
int createServerChannel(char* path){
	int s = 0;
	struct sockaddr_un sock_a; //cosa comporta che venga dichiarata qui?
	/* controllo correttezza del path */
	if (!path){
		errno = EINVAL;
		return -1;
	}

	if(strlen(path) >= UNIX_PATH_MAX) // >= o >?
		return SNAMETOOLONG;
	//necessario?
	//(void)unlink(path);//void?
	//copiare? o semplicemente metterci il puntatore a path?
	//nel caso di copia, fare free su *path?
	sock_a.sun_family = AF_UNIX;
	strncpy(sock_a.sun_path,path,UNIX_PATH_MAX); //o path max?
	//sock_a = malloc(sizeof(struct sockaddr_un)); //cast a malloc? EC!!
	//errno dalle funz di lib!!
	s = socket(AF_UNIX, SOCK_STREAM, 0);//vedere bene come gestire l'errore
	if(s == -1){
		return -1; //errno già impostato da socket
	}

	//qui prima c'era strncpy
	//qui prima c'era family
	//s = socket(AF_UNIX,SOCK_STREAM,0);

	//bind(s,(struct sockaddr *)sock_a, sizeof(sock_a));
	//errno da bind
	EC_M1(bind(s,(struct sockaddr *)&sock_a, sizeof(sock_a)));

	if(listen(s,SOMAXCONN) == -1) //rimetterci la maro giusta
		return -1;

	return s;

}



/** Chiude una socket
 *   \param s file descriptor della socket
 *
 *   \retval 0  se tutto ok,
 *   \retval -1  se errore (sets errno)
 */
int closeSocket(int s){
	// da fare free su sockaddr?
	//sock etc, ricontrollare bene cosa cancellare
	//errno settato da close
	return close(s);
}




/** accetta una connessione da parte di un client
 *  \param  s socket su cui ci mettiamo in attesa di accettare la connessione
 *
 *  \retval  c il descrittore della socket su cui siamo connessi
 *  \retval  -1 in casi di errore (sets errno)
 */
int acceptConnection(int s){
	//errno settato da accept
	return accept(s, NULL,0); //ok ma rivedersi i parametri
}



/** legge un messaggio dalla socket
 *  \param  sc  file descriptor della socket
 *  \param msg  struttura che conterra' il messagio letto
 *		(deve essere allocata all'esterno della funzione,
 *		tranne il campo buffer)
 *
 *  \retval lung  lunghezza del buffer letto, se OK
 *  \retval SEOF  se il peer ha chiuso la connessione
 *                   (non ci sono piu' scrittori sulla socket)
 *  \retval  -1    in tutti gl ialtri casi di errore (sets errno)
 *
 */
int receiveMessage(int sc, message_t * msg){

	unsigned int bytes = 0;

	/*Lettura e controllo del tipo del messaggio*/
	bytes = read(sc, &(msg->type), sizeof(char));
	if(bytes == 0)
		return SEOF;
	if(bytes == -1) //errno dalla read
		return -1;

	/*Lettura della lunghezza del messaggio*/
	bytes = 0;

	//iterare su unsigned int o int normale?
	//come controllare gli errori sulla read qui?
	while( (bytes += read(sc, &(msg->length), sizeof(unsigned int) ) )< sizeof(unsigned int)){
		if (bytes == 0)
			return SEOF;
		if (bytes == -1) //rifare con la macro?
			return -1;//errno dalla read
	}
	/*In caso di fallimento nell'allocazione di memoria restituisce -1, errno settata da */
	EC_NULL_M1(msg->buffer = calloc(msg->length + 1, sizeof(char)));

	/*Riempimento del buffer */
	bytes = 0;
	while( (bytes += read(sc, msg->buffer, msg->length)) < msg->length){
		if(bytes == 0)
			return SEOF;
		if(bytes == -1) //errno dalla read
			return -1;
	}
	return bytes;

}


/** scrive un messaggio sulla socket
 *   \param  sc file descriptor della socket
 *   \param msg struttura che contiene il messaggio da scrivere
 *
 *   \retval  n    il numero di caratteri inviati (se scrittura OK)
 *   \retval  SEOF se il peer ha chiuso la connessione
 *                   (non ci sono piu' lettori sulla socket)
 *   \retval -1   in tutti gl ialtri casi di errore (sets errno)

 */
int sendMessage(int sc, message_t *msg){

	int bytes = 0;

	/*Controllo sul messaggio*/
	if (msg == NULL){
		errno = EINVAL;
		return -1;
	}

	// controllo di -1 aggiunto succesivamente
	//FORSE INUTILE VISTO  il controllo sulla dimensione dei dati da inviare

	/*Invio "metadati" del messaggio*/
	bytes = write(sc,&(msg->type), sizeof(char));

	if(bytes == -1)
		return -1;
	if (bytes == 0)
		return SEOF;
	if(bytes < sizeof(char))
		return -1;

	bytes = write(sc,&(msg->length), sizeof(unsigned int));
	if(bytes == -1)
		return -1;
	if(bytes < sizeof(unsigned int))
		return -1;

	/*Invio del payload del messaggio*/
	bytes = write(sc, msg->buffer, msg->length);
	if(bytes == -1)
		return -1;
	if (bytes == 0)
		return SEOF;
	if( bytes < msg->length)
		return -1;

	//else ?!??!?!?!
	return bytes;
}


/** crea una connessione all socket del server. In caso di errore funzione tenta NTRIALCONN volte la connessione (a distanza di 1 secondo l'una dall'altra) prima di ritornare errore.
 *   \param  path  nome del socket su cui il server accetta le connessioni
 *
 *   \return 0 se la connessione ha successo
 *   \retval SNAMETOOLONG se il nome del socket eccede UNIX_PATH_MAX
 *   \retval -1 negli altri casi di errore (sets errno)
 *
 *  in caso di errore ripristina la situazione inziale: rimuove eventuali socket create e chiude eventuali file descriptor rimasti aperti
 */
int openConnection(char* path){

	int sc;
	int trials;
	struct sockaddr_un sa;


	if(path == NULL){
		errno = EINVAL;
		return -1;
	}

	if(strlen(path) >= UNIX_PATH_MAX)
		return SNAMETOOLONG;

	sa.sun_family = AF_UNIX;
	strncpy(sa.sun_path, path, UNIX_PATH_MAX);

	sc = socket(AF_UNIX, SOCK_STREAM, 0);//come distruggerlo?
	EC_M1(sc);

	/*Si tenta la connessione al socket*/
	for(trials = 0; trials < NTRIALCONN; trials++){
		/*Connect riuscita*/
		if (connect(sc, (struct sockaddr *) &sa, sizeof(sa)) == 0){
			return sc;
		}
		sleep(1);
	} //fine ciclo connessioni

	/*Impossibile stabilire una connessione al socket, errno settato da connect*/
	return -1;
}


